/* 使用指令％skeleton "lalr1.cc"选择C++解析器的骨架 */
%skeleton "lalr1.cc"

/* 指定bison的版本 */
%require "3.0.4"

%define api.namespace {Marker} //声明命名空间与下面声明的类名结合使用 Marker::Parser::  在scanner.l中有体现
%define api.parser.class { Parser }
%define api.token.constructor
%define api.value.type variant //使得类型与token定义可以使用各种复杂的结构与类型
%define parse.assert  //开启断言功能
%defines  //生成各种头文件  location.hh position.hh  parser.hpp
%code requires
{
  /*requires中的内容会放在YYLTYPE与YYSTPYPE定义前*/
  #include <iostream>
  #include <string>
  #include <vector>
  #include <stdint.h>
  #include <cmath>
  using namespace std;

  namespace Marker { /*避免包含头文件时冲突*/
    class Scanner;
    class Driver;
  }
}

%code top
{
  /*尽可能放在parser.cpp靠近头部的地方，与requires相似*/
  #include <iostream>
  #include "scanner.h"
  #include "parser.hpp"
  #include "driver.h"
  #include "location.hh"

  #include "log.h"

  /*注意：这里的参数由%parse-param决定*/
  static Marker::Parser::symbol_type yylex(Marker::Scanner& scanner,Marker::Driver &driver){
    return scanner.nextToken();
  }
  using namespace Marker;
}

/*定义parser传给scanner的参数*/
%lex-param { Marker::Scanner& scanner }
%lex-param { Marker::Driver& driver }

/*定义driver传给parser的参数*/
%parse-param { Marker::Scanner& scanner }
%parse-param { Marker::Driver& driver }

%locations
//%define parse-trace

/*详细显示错误信息*/
%define parse.error verbose

/*通过Marker::Parser::make_XXX(loc)给token添加前缀*/
%define api.token.prefix {TOKEN_}

%token END

%token<string>IDENTIFIER
%token<int>INTEGER

%token<char> CHAR_ADD
%token<char> CHAR_MINUS
%token<char> CHAR_EQUAL
%token<char> CHAR_LPARENTHESES
%token<char> CHAR_RPARENTHESES
%token<char> CHAR_EXCLAMATION_POINT
%token<char> CHAR_TILDE
%token<char> CHAR_TIMES
%token<char> CHAR_SLASH
%token<char> CHAR_MOD
%token<char> CHAR_AND
%token<char> CHAR_XOR
%token<char> CHAR_OR
%token<char> CHAR_QUESTION_MARK
%token<char> CHAR_COLON

%token<string> OP_INC OP_DEC OP_LSL OP_LSR
%token<string> OP_LT OP_LE OP_GT OP_GE
%token<string> OP_EQ OP_NE
%token<string> OP_AND OP_OR
%token<string> OP_ASSIGN

%type<int> expression
%type<int> statement

%right CHAR_EQUAL OP_ASSIGN
%right CHAR_QUESTION_MARK CHAR_COLON
%left OP_OR
%left OP_AND
%left CHAR_OR
%left CHAR_XOR
%left CHAR_AND
%left OP_EQ OP_NE
%left OP_LT OP_LE OP_GT OP_GE
%left OP_LSL OP_LSR
%left CHAR_ADD CHAR_MINUS
%left CHAR_TIMES CHAR_SLASH CHAR_MOD
%right CHAR_EXCLAMATION_POINT CHAR_TILDE OP_INC OP_DEC
%left CHAR_LPARENTHESES CHAR_RPARENTHESES

%start statement

%%

statement: END {
     return 0;
}

statement: expression END {
    $$ = $1;
    driver.result = $$;
    mylog::debug("statement %d", $1);
    return 0;
};

expression: IDENTIFIER CHAR_EQUAL expression {
    driver.variables[$1] = $3;
    $$ = $3;
    mylog::debug("%s %c %d", $1.c_str(), $2, $3);
};
expression: IDENTIFIER OP_ASSIGN expression {
    switch ($2[0]) {
        case '+':
            driver.variables[$1] += $3;
            break;

        case '-':
            driver.variables[$1] -= $3;
            break;

        case '*':
            driver.variables[$1] *= $3;
            break;

        case '/':
            if ($3 == 0) {
                mylog::warn("div 0 error");
                $$ = 0;
                driver.result = $$;
                return 1;
            }
            driver.variables[$1] /= $3;
            break;

        case '%':
            if ($3 == 0) {
                mylog::warn("div 0 error");
                $$ = 0;
                driver.result = $$;
                return 1;
            }
            driver.variables[$1] %= $3;
            break;

        case '&':
            driver.variables[$1] &= $3;
            break;

        case '|':
            driver.variables[$1] |= $3;
            break;

        case '^':
            driver.variables[$1] ^= $3;
            break;

        case '<':
            driver.variables[$1] <<= $3;
            break;

        case '>':
            driver.variables[$1] >>= $3;
            break;

    }
    $$ = driver.variables[$1];
    mylog::debug("%s %s %d", $1.c_str(), $2.c_str(), $3);
};
expression: expression CHAR_QUESTION_MARK expression CHAR_COLON expression {
    $$ = $1 ? $3 : $5;
    mylog::debug("%d %c %d %c %d", $1, $2, $3, $4, $5);
};
expression: expression OP_AND expression {
    $$ = $1 && $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_OR expression {
    $$ = $1 || $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression CHAR_AND expression {
    $$ = $1 & $3;
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: expression CHAR_XOR expression {
    $$ = $1 ^ $3;
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: expression CHAR_OR expression {
    $$ = $1 | $3;
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: expression OP_EQ expression {
    $$ = $1 == $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_NE expression {
    $$ = $1 != $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_LT expression {
    $$ = $1 < $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_LE expression {
    $$ = $1 <= $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_GT expression {
    $$ = $1 > $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_GE expression {
    $$ = $1 >= $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_LSL expression {
    $$ = $1 << $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression OP_LSR expression {
    $$ = $1 >> $3;
    mylog::debug("%d %s %d", $1, $2.c_str(), $3);
};
expression: expression CHAR_ADD expression {
    $$ = $1 + $3;
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: expression CHAR_MINUS expression {
    $$ = $1 - $3;
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: expression CHAR_TIMES expression {
    $$ = $1 * $3;
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: expression CHAR_SLASH expression {
    if ($3 == 0) {
        mylog::warn("div 0 error");
        $$ = 0;
        driver.result = $$;
        return 1;
    } else {
        $$ = $1 / $3;
    }
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: expression CHAR_MOD expression {
    if ($3 == 0) {
        mylog::warn("div 0 error");
        $$ = 0;
        driver.result = $$;
        return 1;
    } else {
        $$ = $1 % $3;
    }
    mylog::debug("%d %c %d", $1, $2, $3);
};
expression: CHAR_EXCLAMATION_POINT expression {
    $$ = !$2;
    mylog::debug("%c %d", $1, $2);
};
expression: CHAR_TILDE expression {
    $$ = ~$2;
    mylog::debug("%c %d", $1, $2);
};
expression: OP_INC IDENTIFIER {
    $$ = ++driver.variables[$2];
    mylog::debug("%s %s", $1.c_str(), $2.c_str());
};
expression: OP_DEC IDENTIFIER {
    $$ = --driver.variables[$2];
    mylog::debug("%s %s", $1.c_str(), $2.c_str());
};
expression: CHAR_ADD expression %prec CHAR_EXCLAMATION_POINT {
    $$ = $2;
    mylog::debug("%c %d", $1, $2);
};
expression: CHAR_MINUS expression %prec CHAR_EXCLAMATION_POINT {
    $$ = -$2;
    mylog::debug("%c %d", $1, $2);
};
expression: CHAR_LPARENTHESES expression CHAR_RPARENTHESES {
    $$ = $2;
    mylog::debug("%c %d %c", $1, $2, $3);
};
expression: INTEGER {
    $$ = $1;
    mylog::debug("INTEGER %d", $1);
};
expression: IDENTIFIER {
    $$ = driver.variables[$1];
    mylog::debug("IDENTIFIER get %s, val = %d", $1.c_str(), driver.variables[$1]);
};

%%
/*Parser实现错误处理接口*/
void Marker::Parser::error(const Marker::location& location,const std::string& message){
  std::cout<<"msg:"<<message
           <<", error happened at: "<<location<<std::endl;
}
